/*
 * @Author: LVGRAPE
 * @LastEditors: LVGRAPE
 */
#include "bootloader.h"
#include "at32_mcu_info.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#define CRC32_INIT_VALUE 0xFFFFFFFF
#define CRC32_XOR_VALUE 0xFFFFFFFF


uint32_t crc32_cal(const uint8_t *pdata, int32_t len, uint32_t initial, uint32_t finalXor, bool inputReflected, bool resultReflected);

/**
 * @brief
 *  make -f ./win_iap_tool/firminfo.mk
 * ./win_iap_tool/makefirmware.exe ./win_iap_tool/ZINO_FC_V4_V1.0.1_20241115.bin test
 * @param argc
 * @param argv
 * @return int
 */
int main(int argc, char *argv[])
{
    char *newName = NULL;
    if (argc < 2)
    {
        printf("Usage: %s <*.bin> <newName(optional)> %d\n", argv[0], argc);
        return -1;
    }
    if (argc == 3)
    {
        newName = argv[2];
    }
    firmware_info_t firmInfo;
    // firmInfo.DeviceID = AT32F421C8T7_64KB_LQFP48;
    FILE *fw = fopen(argv[1], "rb");
    if (fw == NULL)
    {
        printf("open file error\n");
        return -1;
    }

    size_t thisRead = fread(&firmInfo, 1, sizeof(firmInfo), fw);
    if (thisRead != sizeof(firmInfo))
    {
        printf("read firmInfo size error\n");
        return -1;
    }
    printf("name:%s\n", firmInfo.name);
    printf("infoCrc: 0x%08X\n", firmInfo.infoCrc);
    printf("magic: 0x%08X\n", firmInfo.magic);
    printf("startAddr: 0x%08X\n", firmInfo.start_addr);
    printf("size: 0x%08X\n", firmInfo.size);
    printf("version: 0x%08X\n", firmInfo.version);
    printf("pid: 0x%08X\n", firmInfo.pid);
    printf("date: 0x%08X\n", firmInfo.date);
    printf("DeviceID: 0x%016X\n", firmInfo.DeviceID);
    // printf("ID: 0x%08X\n", firmInfo.whoAmI.ID);
    // printf("Model: 0x%08X, %s\n", firmInfo.whoAmI.Model, at32_mcu_mode2str(&firmInfo.whoAmI));
    // printf("Flash: 0x%08X, %s\n", firmInfo.whoAmI.Flash, at32_mcu_flash2str(&firmInfo.whoAmI));
    // printf("Footprint: 0x%08X, %s\n", firmInfo.whoAmI.Footprint, at32_mcu_footprint2str(&firmInfo.whoAmI));

    if (firmInfo.magic != FLASH_FW_MAGIC && firmInfo.infoCrc != ~FLASH_FW_MAGIC)
    {
        printf("firmware magic error\n");
        return -1;
    }
    // if (firmInfo.DeviceID >= AT32_MCU_MODEL_COUNT)
    // {
    //     printf("mcu model unknow!\n");
    //     return -1;
    // }
    // AT32_MCU_TYPE mcuType = AT32_MCU_LIST[firmInfo.whoAmI.Model];
    // if (mcuType.Model != firmInfo.whoAmI.Model)
    // {
    //     printf("mcu model error!\n");
    //     return -1;
    // }
    // if (mcuType.Flash != firmInfo.whoAmI.Flash)
    // {
    //     printf("mcu flash error!\n");
    //     return -1;
    // }
    // if (mcuType.Footprint != firmInfo.whoAmI.Footprint)
    // {
    //     printf("mcu footprint error!\n");
    //     return -1;
    // }
    // if (mcuType.Serial != firmInfo.whoAmI.Serial)
    // {
    //     printf("mcu Serial error!\n");
    //     return -1;
    // }
    // if (mcuType.ID != firmInfo.whoAmI.ID)
    // {
    //     printf("mcu ID error!\n");
    //     return -1;
    // }

    fseek(fw, 0, SEEK_SET);
    fseek(fw, 0, SEEK_END);
    size_t fwSize = ftell(fw);
    size_t fwTotalsize = fwSize - sizeof(firmInfo);
    size_t fwEndAddr = firmInfo.start_addr + firmInfo.size;
    fseek(fw, 0, SEEK_SET);
    printf("fwTotalsize(without fwInfo): 0x%08X\n", fwTotalsize);
    size_t flashToalSize = at32_get_flash_size(firmInfo.DeviceID);
    size_t flashEndAddr = firmInfo.start_addr + flashToalSize;
    printf("flashToalSize: %d,[0x%08X-0x%08X]\n", flashToalSize, FLASH_Base_Addr, flashEndAddr);
    if (fwEndAddr > flashEndAddr)
    {
        printf("firmware size overflow!\n");
        return -1;
    }
    firmInfo.size = fwTotalsize;
    uint8_t *fwBuf = (uint8_t *)malloc(fwSize);
    thisRead = fread(fwBuf, 1, fwSize, fw);
    if (thisRead != fwSize)
    {
        printf("firmware read fwSize error!\n");
        return -1;
    }
    /**use new name */
    if (newName)
    {
        memset(firmInfo.name, 0, sizeof(firmInfo.name));
        memcpy(firmInfo.name, newName, strlen(newName));
        printf("get new name: %s\n", firmInfo.name);
    }
    char *newFwname;
    size_t newFwnameLen = strlen(firmInfo.name);
    const char *src= firmInfo.name;
    if(strcmp(&src[newFwnameLen - 4], ".bin") != 0)
    {
        newFwname = (char *)malloc(newFwnameLen + 5);
        strcpy(newFwname, src);
        strcat(newFwname, ".bin");
        printf("newFwname: %s\n", newFwname);
    }
    else
    {
        newFwname = (char *)malloc(newFwnameLen + 1);
        strcpy(newFwname, src);
        printf("newFwname: %s\n", newFwname);
    }

    /**update fw crc, info crc */
    firmInfo.FWcrc = crc32_cal(&fwBuf[FLASH_SectorSize], fwTotalsize, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
    firmInfo.infoCrc = crc32_cal((const uint8_t *)&firmInfo+4, sizeof(firmInfo) - 4, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
    printf("FWcrc: 0x%08X, infoCrc:0x%08X\n", firmInfo.FWcrc, firmInfo.infoCrc);
    /**updata firmInfo with crc*/
    memcpy(fwBuf, &firmInfo, sizeof(firmInfo));
    /**write new firmware */



    FILE *fwOut = fopen(newFwname, "wb");
    if (!fwOut)
    {
        printf("new firmware creat error!\n");
        return -1;
    }
    size_t thisWrite = fwrite(fwBuf, 1, fwSize, fwOut);
    if (thisWrite != fwSize)
    {
        printf("firmware write fwSize error!\n");
        return -1;
    }
    printf("Success! New: %s\n", newFwname);
    fclose(fwOut);
    free(fwBuf);
    return 0;
}
